// Audio.cpp: implementation of the CAudio class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Audio.h"
#include <mmsystem.h>
#include "audiodef.h"
#include <process.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAudio::CAudio()
{
	deviceID = 0;
	bAudioAvailable = FALSE;
	nCodec = 0; //AUDIO_PCM;
	//myWaveFormat;//tblWaveFormat[PCM_16BIT_8KHZ_MONO];
	myWaveFormat.cbSize = sizeof(WAVEFORMATEX);
	PacketSize = 0;
	dwAudioFormat = 0;//MAKELONG( PCM_16BIT_8KHZ_MONO , AUDIO_PCM );
	bInitialized = FALSE;
	m_PlayerInfo = NULL;
	m_hPlayer = NULL;
	m_bRunThread = FALSE;
	m_bPlay = FALSE;
	m_bMute = TRUE;
	m_nSampleFormat = 0;
	m_pSoundPlayer = NULL;

}

CAudio::~CAudio()
{
	Finalize();
}

BOOL CAudio::Initialize(HWND hWnd)
{
	bAudioAvailable = FALSE;
	bInitialized = FALSE;
	if( m_pSoundPlayer == NULL )
	{
#if defined(_DXSOUND)
		m_pSoundPlayer = new CAudioDX();
#else
		m_pSoundPlayer = new CAudioWM();
#endif
	}
	
	if( m_pSoundPlayer->InitializeAudio(hWnd) != S_OK)
	{
		return FALSE;
	}

	UINT tid;
	bInitialized = TRUE;
	m_bRunThread = TRUE;
	m_PlayerInfo = m_pSoundPlayer->GetPlayerInfo();
	//((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

	m_hPlayer = (HANDLE)_beginthreadex(NULL,0,AudioPlayerThreadProc,(void*)this, 0, &tid );

	return TRUE;
}

void CAudio::Finalize()
{
	
	if( bAudioAvailable )
	{
		

#ifdef _DXSOUND
		if( m_pSoundPlayer )m_pSoundPlayer->StopPlayer((XAUDIOINFO*)m_PlayerInfo);
		if( m_pSoundPlayer )m_pSoundPlayer->FinalizePlayer( (XAUDIOINFO*)m_PlayerInfo );
#else
		if( m_pSoundPlayer )m_pSoundPlayer->StopPlayer((XAUDIOINFO*)m_PlayerInfo);
		if( m_pSoundPlayer )m_pSoundPlayer->FinalizePlayer( (XAUDIOINFO*)m_PlayerInfo );
#endif
	}

	XAUDIOPACKET * pkt = NULL;
	int nSize = 0;
	int nCount=0;
	while( nSize = m_lstAudioPacket.size() )
	{
		pkt = m_lstAudioPacket.front();
		m_lstAudioPacket.pop_front();	
		delete pkt;
		nCount++;
	}

	bAudioAvailable = FALSE;

	if( bInitialized )
	{
		if( m_pSoundPlayer )m_pSoundPlayer->FinalizeAudio();
	}

	bInitialized = FALSE;
	m_bRunThread = FALSE;

	if( m_PlayerInfo )
	{
		if( m_pSoundPlayer )SetEvent(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent);
	}

	if( m_hPlayer )
	{
		WaitForSingleObject(m_hPlayer , 3000);
	}
	m_hPlayer = NULL;
	m_bPlay = FALSE;

	if( m_PlayerInfo) {
		if( m_pSoundPlayer ) m_pSoundPlayer->ReleasePlayerInfo( (XAUDIOINFO*)m_PlayerInfo);
	}

	if( m_pSoundPlayer )
	{
		delete m_pSoundPlayer;
		m_pSoundPlayer = NULL;
	}
	m_PlayerInfo = NULL;
}

BOOL CAudio::IsAudioAvailable()
{
	return bAudioAvailable;
}

void CAudio::SetVolume(int vol)
{
	if (!bAudioAvailable)
		return;

	DWORD wvol = vol * 0xFFFF / 100;
	DWORD dwvol = (wvol<<16)|wvol;

	switch (deviceID)
	{
	case 0:
		waveOutSetVolume( NULL, dwvol );
		break;
	case 1:
		waveOutSetVolume( (HWAVEOUT)WAVE_MAPPER, dwvol );
		break;
	}
}

int CAudio::GetVolume()
{
	if (!bAudioAvailable)
		return FALSE;

	DWORD dwvol = 0;
	if( MMSYSERR_NOERROR==waveOutGetVolume( NULL, &dwvol ) )
	{
		deviceID = 0;
	}
	else if( MMSYSERR_NOERROR==waveOutGetVolume( (HWAVEOUT)WAVE_MAPPER, &dwvol ) )
	{
		deviceID = 1;
	}
	else
	{
		deviceID = 0;
		TRACE("waveOutGetVolume error\n" );
	}

	return int(((dwvol&0xFFFF)*100)>>16);
}

void CAudio::SetCodec(DWORD dwformat, int packet_size)
{
	XAUDIOINFO *ppi = (XAUDIOINFO*)m_PlayerInfo;
	
	if( dwformat == dwAudioFormat && packet_size == PacketSize && bAudioAvailable )
		return;

//typedef struct tWAVEFORMATEX
//{
//    WORD        wFormatTag;         /* format type */
//    WORD        nChannels;          /* number of channels (i.e. mono, stereo...) */
//    DWORD       nSamplesPerSec;     /* sample rate */
//    DWORD       nAvgBytesPerSec;    /* for buffer estimation */
//    WORD        nBlockAlign;        /* block size of data */
//    WORD        wBitsPerSample;     /* number of bits per sample of mono data */
//    WORD        cbSize;             /* the count in bytes of the size of */
//				
//} WAVEFORMATEX
	
	if( m_bPlay )
	{

		if( StopPlay() == FALSE )
			return;
//		if( m_pSoundPlayer->StopPlayer(ppi) != S_OK )
//		{
//			return;
//		}
//		while( m_pSoundPlayer->StopPlayer(ppi) != S_OK );
	}
	
	if( bAudioAvailable )
	{
		m_pSoundPlayer->FinalizePlayer(ppi);
	}
	
	m_bPlay = FALSE;

	bAudioAvailable = FALSE;

	PacketSize = packet_size;

	dwAudioFormat = dwformat;
	nCodec = HIWORD(dwformat);
	int sample = LOWORD(dwformat);
	m_nSampleFormat = sample;
	myWaveFormat = tblWaveFormat[sample];
	ppi->nCodec = nCodec;
	ppi->nSampleFormat = sample;

	m_pSoundPlayer->SetPacketSize( PacketSize );
	if( m_pSoundPlayer->InitializePlayer( ppi ) != S_OK )
	{
		//Finalize();
		return;
	}
//
//	if( m_pSoundPlayer->StartPlayer( ppi ) != S_OK )
//		return;
//	m_bPlay = TRUE;
	
	bAudioAvailable = TRUE;
}

BOOL CAudio::AudioOut(BYTE *data, int len, FILETIME *ftStart)
{
	XAUDIOPACKET * pkt = NULL;

	if( bAudioAvailable == FALSE)
		return FALSE;

	TRACE("[AUDIO] qsize %d\n", GetQueueSize());
	if( GetQueueSize()==DEFAULT_MAXQUEUE_SIZE )
		return FALSE;

	pkt = Dequeue();
	if( !pkt )
	{
//		LOGF("[AUDIO] not enough packet for play\n");
//		return FALSE;
		pkt = new XAUDIOPACKET;
		memset(pkt,0, sizeof(XAUDIOPACKET));
	}

	pkt->channel = 0;
	memcpy(pkt->data, data, len);
	pkt->fmt = dwAudioFormat;
	pkt->length = len;
	memcpy(&pkt->time,ftStart,sizeof(FILETIME));
	pkt->next = NULL;

	memcpy( pkt->data, data, (len>DEFAULT_PACKET_SIZE)?DEFAULT_PACKET_SIZE:len );

//	if( t ) memcpy( &pkt->time, t, sizeof(FILETIME) );
//	SYSTEMTIME st;
//	FileTimeToSystemTime( &pkt->time , &st);
//	TRACE("[AUDIOPLAYER] %04d-%02d-%02d %02d:%02d:%02d:%03d PACKETLEN=%d \n", 
//		st.wYear, st.wMonth, st.wDay, st.wHour,st.wMinute,st.wSecond, st.wMilliseconds, pkt->length);
	XAUDIOPACKET * pop = Enqueue( pkt );

	if( m_bPlay == FALSE )
	{
//		if( m_bPlay == FALSE ) StartPlay();
		if(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent) 
			SetEvent(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent );
	}

	return TRUE;

//	if( bAudioAvailable == FALSE)
//		return FALSE;
//
//	pkt = new XAUDIOPACKET;
//	memset(pkt,0, sizeof(XAUDIOPACKET));
//	pkt->channel = 0;
//	memcpy(pkt->data, data, len);
//	pkt->fmt = dwAudioFormat;
//	pkt->length = len;
//	//memcpy(&pkt.time,ftStart,sizeof(FILETIME));
//	pkt->next = NULL;
//	csBuffer.Lock();
//	m_lstAudioPacket.insert( m_lstAudioPacket.end(), pkt );
//	int nSize = m_lstAudioPacket.size();
//	csBuffer.Unlock();
//	if( m_bPlay == FALSE && nSize > 2 )
//	{
////		if( m_bPlay == FALSE ) StartPlay();
//		if(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent) 
//			SetEvent(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent );
//	}

//	XAUDIOPACKET pkt;
//	if( bAudioAvailable == FALSE)
//		return FALSE;
//
//	memset(&pkt,0, sizeof(XAUDIOPACKET));
//	pkt.channel = 0;
//	memcpy(pkt.data, data, len);
//	pkt.fmt = dwAudioFormat;
//	pkt.length = len;
//	//memcpy(&pkt.time,ftStart,sizeof(FILETIME));
//	pkt.next = NULL;
//
//	if( !m_bPlay )
//	{
//		if( !StartPlay() )
//			return FALSE;
//		m_bPlay = TRUE;
//	}
//
//	if( m_bMute == FALSE) 
//	{
//		m_pSoundPlayer->PlayerWritePacket((XAUDIOINFO*)m_PlayerInfo, &pkt );	
//		WaitForSingleObject(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent, 100000-myWaveFormat.nSamplesPerSec );
//	}


	return TRUE;
}


BOOL CAudio::IsInitialized()
{
	return bInitialized;
}

unsigned int WINAPI CAudio::AudioPlayerThreadProc(LPVOID lParam)
{
	CAudio * pAudio = (CAudio*)lParam;
	XAUDIOINFO * pai = (XAUDIOINFO*) pAudio->m_PlayerInfo;
	XAUDIOPACKET * pkt = NULL;
	int ret;
	int nRet;
	int nCheckBuff = 0;
	int codec;
	int sample;
	FILETIME ftStart;
	int len=DEFAULT_PACKET_SIZE;
	BYTE data[DEFAULT_PACKET_SIZE];
	memset( data,0x80,DEFAULT_PACKET_SIZE);

	XAUDIOPACKET silence;
	//g711_s2u(data,silence.data, 440);

	while(1)
	{
		nRet = WaitForSingleObject( pai->hNotificationEvent, 30 );

		if( pAudio->m_bRunThread == FALSE )
			break;

		if( nRet != WAIT_OBJECT_0 )
		{
#if defined(_DXSOUND)
			continue;
#else
			nRet = WaitForSingleObject( pAudio->m_hQueueEvent, 10 );
			if( nRet != WAIT_OBJECT_0 )
			{
				continue;
			}
#endif
		}

		if( pAudio->GetQueueSize()>0 )
		{
			if( pAudio->m_bPlay== FALSE )
			{
				pAudio->StartPlay();
			}
			if( pkt = pAudio->Dequeue() )
			{
				codec = AUDIOPKT_GET_CODEC(pkt);
				sample = AUDIOPKT_GET_SAMPLE(pkt);


				if( AUDIOPKT_IS_DATA(pkt) )
				{
					if( pai->nCodec!=codec ||
						pai->nSampleFormat!=sample ) 
					{
						pAudio->SetCodec( pkt->fmt, pkt->length );
						len = pkt->length;
						ftStart = pkt->time;

						if( pAudio->m_bPlay== FALSE )
						{
							pAudio->StartPlay();
						}
					}

					if( FAILED( pAudio->m_pSoundPlayer->PlayerWritePacket( pai, pkt ) ) )
					{
						TRACE( _T("PlayWritePacket error\n"));
//						ret++;
						//break;
					}
				}

				delete pkt;
				pkt	= NULL;
				//TRACE(_T("Played\n"));
			}
		}
		if(0) //else
		{
			//TRACE(_T("no packet available, filling empty\n"));
			pkt = pAudio->Dequeue();
			if( pkt )
			{
				pkt->length = 0; // notifying blank data
				if( FAILED( pAudio->m_pSoundPlayer->PlayerWritePacket( pai, pkt ) ) )
				{
					TRACE(_T("PlayWritePacket error\n"));
					ret++;
					break;
				}
			}
			else
			{
				TRACE(_T("out of packet in playing silence===================================\n"));

//				silence.fmt =MAKELONG( PCM_16BIT_8KHZ_MONO , AUDIO_PCM );
//				silence.length = 250;
//				silence.next = NULL;
				memset( silence.data,0x00,DEFAULT_PACKET_SIZE);
//
//				codec = AUDIOPKT_GET_CODEC(&silence);
//				sample = AUDIOPKT_GET_SAMPLE(&silence);
//
//
//				if( AUDIOPKT_IS_DATA(&silence) )
//				{
//					if( pai->nCodec!=codec ||
//						pai->nSampleFormat!=sample ) // changed to pcm
//					{
//						pAudio->SetCodec( silence.fmt, silence.length );
//					}
//
//					if( pAudio->m_bPlay== FALSE )
//					{
//						pAudio->StartPlay();
//					}
//
					silence.fmt = MAKELONG( sample, codec );

					silence.length = 0;
					if( FAILED( pAudio->m_pSoundPlayer->PlayerWritePacket( pai, &silence ) ) )
					{
						TRACE( _T("PlayWritePacket error\n"));
						ret++;
						//break;
					}
//				}

//				pAudio->StopPlay();
			}
		}

	}

//	int nRet;
//	int nSize;
//	int nCheckBuff = 0;
//	while(1)
//	{
//		nRet = WaitForSingleObject( pai->hNotificationEvent, 2000 );
//
//		if( pAudio->m_bRunThread == FALSE )
//			break;
//
//		if( nRet != WAIT_OBJECT_0 )
//			continue;
//		
//		pAudio->csBuffer.Lock();
//		nSize = pAudio->m_lstAudioPacket.size();
//		TRACE("AUDIO PACKET SIZE=%d\n", nSize );
//		if( nSize == 0 )
//		{
//			pAudio->csBuffer.Unlock();
//			pkt = NULL;
//			pAudio->StopPlay();
//			continue;
//		}
//
//		if(pAudio->m_bPlay == FALSE /*&& pAudio->m_bMute == FALSE*/)
//			pAudio->StartPlay();
//
//		nCheckBuff = 0;
//		pkt = pAudio->m_lstAudioPacket.front();
//
//		pAudio->m_lstAudioPacket.pop_front();
//		nSize = pAudio->m_lstAudioPacket.size();
//		TRACE("AUDIO PACKET SIZE=%d\n", nSize );		
//		pAudio->csBuffer.Unlock();
//
//		//if( pAudio->m_bMute == FALSE )
//			hRes =  pAudio->m_pSoundPlayer->PlayerWritePacket((XAUDIOINFO*)pAudio->m_PlayerInfo, pkt );
//		//else
//		//	SetEvent(pai->hNotificationEvent);
//		
//		delete pkt;
//		pkt = NULL;
//	}

	_endthreadex(0);
	return 0;

}

BOOL CAudio::StopPlay()
{
	XAUDIOINFO *ppi = (XAUDIOINFO*)m_PlayerInfo;
	XAUDIOPACKET * pkt;
	int nSize;
	int nCount=0;
	csStatus.Lock();
	if( ppi && m_bPlay )
	{
		if( m_pSoundPlayer && m_pSoundPlayer->StopPlayer(ppi) != S_OK )
		{
			TRACE("SOUND STOP FAIL\n");
			m_bPlay = FALSE;
			csStatus.Unlock();
			return FALSE;
		}
	}

	csBuffer.Lock();
	while( nSize = m_lstAudioPacket.size() )
	{
		pkt = m_lstAudioPacket.front();
		m_lstAudioPacket.pop_front();	
		delete pkt;
		TRACE("PACKET DELETE:%d\n",nCount++);
	}

	csBuffer.Unlock();

	Flush();
	m_bPlay = FALSE;
	csStatus.Unlock();
	return TRUE;
}

BOOL CAudio::StartPlay()
{
	if( bAudioAvailable == FALSE ) return FALSE;
	csStatus.Lock();
	XAUDIOINFO *ppi = (XAUDIOINFO*)m_PlayerInfo;
	if( ppi && !m_bPlay )
		if( m_pSoundPlayer->StartPlayer( ppi) != S_OK )
		{
			csStatus.Unlock();
			return FALSE;
		}

	m_bPlay = TRUE;
	csStatus.Unlock();
	return TRUE;
}

void CAudio::SetMute(BOOL bMute)
{
	if( bMute == TRUE )
		StopPlay();
	m_bMute = bMute;
}

BOOL CAudio::AudioOut(DWORD codec, DWORD sample, BYTE *buf, int len, FILETIME * ftStart )
{
	XAUDIOPACKET * pkt = NULL;

	if( bInitialized == FALSE)
		return FALSE;

	TRACE("[AUDIO] qsize %d\n", GetQueueSize());
	if( GetQueueSize() >= DEFAULT_MAXQUEUE_SIZE )
		return FALSE;

	//pkt = Dequeue();
	if( !pkt )
	{
//		LOGF("[AUDIO] not enough packet for play\n");
//		return FALSE;
		pkt = new XAUDIOPACKET;
		memset(pkt,0, sizeof(XAUDIOPACKET));
	}

	pkt->channel = 0;
	//memcpy(pkt->data, buf, len);
	pkt->fmt = dwAudioFormat = MAKELONG(sample, codec);
	pkt->length = len;
	memcpy(&pkt->time,ftStart,sizeof(FILETIME));
	pkt->next = NULL;

	if( len < DEFAULT_PACKET_SIZE )
		memcpy( pkt->data, buf, len );
	else
	{
		memcpy( pkt->data, buf, DEFAULT_PACKET_SIZE );
		pkt->length = DEFAULT_PACKET_SIZE;
	}

	if( m_bPlay == FALSE )
	{
//		if( m_bPlay == FALSE ) StartPlay();
//		if(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent) 
//			SetEvent(((XAUDIOINFO*)m_PlayerInfo)->hNotificationEvent );
		SetCodec( dwAudioFormat, len );

		if( StartPlay() == FALSE)
			return FALSE;

	}

//	if( t ) memcpy( &pkt->time, t, sizeof(FILETIME) );
//	SYSTEMTIME st;
//	FileTimeToSystemTime( &pkt->time , &st);
//	TRACE("[AUDIOPLAYER] %04d-%02d-%02d %02d:%02d:%02d:%03d PACKETLEN=%d \n", 
//		st.wYear, st.wMonth, st.wDay, st.wHour,st.wMinute,st.wSecond, st.wMilliseconds, pkt->length);
	XAUDIOPACKET * pop = Enqueue( pkt );


	return TRUE;
}
